using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Xml.Serialization;
using System.IO;

namespace Colman.Rpg.Npc
{
    /// <summary>
    /// this class repransent a npc world 
    /// </summary>
    [Serializable]
    public class World
    {
        private const float ChanceOfImpassableTile = 0.1f;

        private TileType[][] mapObjects;        

        /// <summary>
        /// Creates a new instance of <see cref="World" />.
        /// </summary>
        /// <param name="size">The size of one side of the world (the world is square).</param>
        /// <param name="chanceOfMonster">The chance of encountering a monster (0.0 to 1.0).</param>
        public World(int size, double chanceOfMonster)
        {
            // chanceOfMonster must be between 0.0 and 1.0.
            if (chanceOfMonster > 1 || chanceOfMonster < 0)
            {
                throw new ArgumentOutOfRangeException("chanceOfMonster", "Chance of monster must be a percentage.");
            }

            // size must be positive
            if (size <= 0)
            {
                throw new ArgumentOutOfRangeException("size", "Size must be a positive number.");
            }            

            // create the world surface 
            mapObjects = new TileType[size][];
            Random randomGenerator = new Random();
            bool playerCreatedYet = false;

            // go over the world surface and initalize the squares in the surface
            for (int i = 0; i < size; i++)
            {
                mapObjects[i] = new TileType[size];

                for (int j = 0; j < size; j++)
                {
                    double chance = randomGenerator.NextDouble();

                    if (chance <= ChanceOfImpassableTile)
                    {
                        // Create a randomly impassable tile.
                        mapObjects[i][j] = (randomGenerator.NextDouble() > 0.5 ? TileType.Rock : TileType.Tree);
                    }
                    else if (chance - ChanceOfImpassableTile <= chanceOfMonster)
                    {
                        // Create a random monster tile.
                        mapObjects[i][j] = TileType.Monster;
                    }
                    else
                    {
                        if (!playerCreatedYet)
                        {
                            mapObjects[i][j] = TileType.Player;
                            playerPosition = new Point(i, j);
                            playerCreatedYet = true;
                        }
                        else
                        {
                            // Create a simple grass tile.
                            mapObjects[i][j] = TileType.Grass;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets the <see cref="TileType"/> at the given coordinates.
        /// </summary>
        /// <param name="x">The 0 based x coordinate of the map tile.</param>
        /// <param name="y">The 0 based y coordinate of the map tile.</param>
        /// <returns>Tthe <see cref="TileType"/> at the given coordinates.</returns>
        /// <exception cref="ArgumentOutOfRangeException">Raised if x or y are less than 0 or greater or equal to the size of the world.</exception>
        public TileType this[int x, int y]
        {
            get
            {
                if (x < 0 || x > this.mapObjects.GetUpperBound(0))
                    throw new ArgumentOutOfRangeException("x");

                if (y < 0 || y > this.mapObjects[0].GetUpperBound(0))
                    throw new ArgumentOutOfRangeException("y");

                return this.mapObjects[x][y];
            }
        }

        /// <summary>
        /// Gets the size of the world.
        /// </summary>
        public int Size
        {
            get
            {
                return this.mapObjects.GetUpperBound(0);
            }
        }

        #region Point PlayerPosition { get; set; } (playerPosition)
        private Point playerPosition;

        /// <summary>
        /// Gets or sets the player's position.
        /// </summary>
        public Point PlayerPosition
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.playerPosition;
            }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (this.playerPosition != value)
                {
                    this.mapObjects[this.playerPosition.X][this.playerPosition.Y] = TileType.Grass;

                    this.playerPosition = value;

                    this.mapObjects[this.playerPosition.X][this.playerPosition.Y] = TileType.Player;

                    this.OnPlayerPositionChanged();
                }
            }
        }

        #region event EventHandler PlayerPositionChanged
        /// <summary>
        /// Invokes the <see cref="PlayerPositionChanged" /> event.
        /// </summary>
        [System.Diagnostics.DebuggerStepThrough]
        protected virtual void OnPlayerPositionChanged()
        {
            if (this.PlayerPositionChanged != null)
                this.PlayerPositionChanged(this, EventArgs.Empty);
        }

        /// <summary>
        /// Raised when the <see cref="PlayerPosition"/> property changes.
        /// </summary>
        public event EventHandler PlayerPositionChanged;
        #endregion
        #endregion
    }
}
